﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace Smtp_server
{
    public class Parser
    {
        public Email email;

        private String DOMAIN;
        private String FROM;
        private String RCTP;
        private String DATA;
        private enum Status
        {
            NULL = 0,
            HELO = 1,
            FROM = 2,
            RCTP = 3,
            DATA = 4,
            DOT = 5,
            QUIT = 6
        };
        private Status status = Status.NULL;
        private String replay = "";


        public bool finish()
        {
            return this.status == Status.QUIT;
        }
        public int getStatus()
        {
            return (int)this.status;
        }

        public String Connection(String stream)
        {
            replay = "";
            switch (status)
            {
                case Status.NULL:
                    if (this.ProcessHELO(stream))
                    {
                        status = Status.HELO;
                    }
                    break;
                case Status.HELO:
                    if (this.ProcessFROM(stream))
                    {
                        status = Status.FROM;
                    }
                    break;
                case Status.FROM:
                    if (this.ProcessRCTP(stream))
                    {
                        status = Status.RCTP;
                    }
                    break;
                case Status.RCTP:
                    if (this.ProcessDATA(stream))
                    {
                        status = Status.DATA;
                    }

                    break;

                case Status.DATA:
                    if (this.ProcessDOT(stream))
                    {
                        status = Status.DOT;
                    }
                    else
                    {
                        this.ProcessDATAbody(stream);
                    }
                    break;
                case Status.DOT:
                    if (this.ProcessQUIT(stream))
                    {
                        status = Status.QUIT;
                    }
                    break;
            }

            //Commands
            if (String.Compare(stream,0, "HELP", 0, 4, true) == 0)
            {
                replay="214 SMTP servers help those who help themselves.\r\n"+
                    "214-Valid SMTP commands:\r\n" +
                    "214- HELO, NOOP, RSET, QUIT,  MAIL, RCPT, DATA, VRFY, HELP\r\n";
            }
            else if (String.Compare(stream,0, "VRFY", 0, 4, true) == 0)
            {

                replay = "252 address verification is not available\r\n";
            }
            else if (String.Compare(stream,0, "RSET", 0,4,true) == 0) 
            {
                status = Status.HELO;
                replay = "250 Reset done.\r\n";
            }
            else if (String.Compare(stream, 0, "NOOP",0, 4,true) == 0)
            {
                replay = "250 NO OPeration.\r\n";
            }

             

            return replay;
        }



        private bool ProcessHELO(String stream)
        {
            //string text = "HELO crepes.fr";
            string pat = @"(^\bHELO\b\s*(?<1>[^\r\n\s]*))" +@"|"+@"(^\bEHLO\b\s*(?<1>[^\r\n\s]*))";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return false;
            DOMAIN = g1.Captures[0].ToString();
            replay = "250 HELLO " + DOMAIN + ", pleased to meet you\r\n";
            return true;
        }

        private bool ProcessFROM(String stream)
        {
            //string text = "MAIL FROM: <alice@crepes.fr>";
            string pat = @"^\bMAIL\b\s\bFROM\b\s*:\s*(\<(?<1>[^\>]+)|(?<1>[^\r\n\s]+))";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return false;
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return false;
            FROM = g1.Captures[0].ToString();

            if (!validAddress(FROM)) {
                replay = "501 FROM: "+FROM+" Invalid mail address\r\n";
                return false;
            }

            replay = "250 " + FROM + " ... Sender ok\r\n";
            return true;
        }
        private bool ProcessRCTP(String stream)
        {
            string pat = @"^\bRCPT\b\s\bTO\b\s*:\s*(\<(?<1>[^\>]+)|(?<1>[^\r\n\s]+))";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return false;
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return false;
            RCTP = g1.Captures[0].ToString();
            if (!validAddress(RCTP))
            {
                replay = "501 RCPT: " + RCTP + " Invalid mail address\r\n";
                return false;
            }
            replay = "250 " + RCTP + " ... Recipient ok\r\n";
            return true;
        }
        private bool ProcessDATA(String stream)
        {
            //DATA
            string pat = @"^\bDATA\b";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return false;
            replay = "354 End data with . on a line by itself\r\n";
            return true;
        }
        private bool ProcessDATAbody(String stream)
        {
            DATA = DATA + stream;
            replay = "";
            return true;
        }
        private bool ProcessDOT(String stream)
        {
            string pat = @"(^\.)" +@"|" +@"(\r\n.\r\n)";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return false;
            DATA += String.Copy( stream.Substring(0,m.Index));
            replay = "250 Ok: Message accepted for delivery\r\n";
            return true;
        }
        
        private bool ProcessQUIT(String stream)
        {
            string pat = @"^\bQUIT\b";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return false;
            replay = @"221 Bye\r\n";
            return true;
        }

        public Email ExtractEmail()
        {
            email = new Email();

            email.Received = ProcessDataReceived(DATA);
            email.From = ProcessDataFrom(DATA);
            email.To = ProcessDataTo(DATA);
            email.Subject = ProcessDataSubject(DATA);
            email.BodyText = ProcessDataBodyText(DATA);
            return email;

        }
        private String ProcessDataBodyText(String stream)
        {
            string pat = @"\r\n\r\n"+@"|"+@"\n\n";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return @"";
            return String.Copy(DATA.Substring(m.Index + "\r\n\r\n".Length));
        }
        private String ProcessDataFrom(String stream)
        {
            string pat = @"FROM\s*:\s+(?<1>[^\r\n\s]+)";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return @"";
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return @"";
            return g1.Captures[0].ToString();
        }
        private String ProcessDataTo(String stream)
        {
            string pat = @"TO\s*:\s+(?<1>[^\r\n]+)";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return @"";
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return @"";
            return g1.Captures[0].ToString();
        }
        private String ProcessDataSubject(String stream)
        {
            string pat = @"Subject\s*:\s+(?<1>[^\r\n]+)";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return @"";
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return @"";
            return g1.Captures[0].ToString();
        }

        private String ProcessDataReceived(String stream)
        {
            string pat = @"Date\s*:\s+(?<1>[^\r\n]+)";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return @"";
            Group g1 = m.Groups[1];
            if (g1.Captures.Count == 0)
                return @"";
            return g1.Captures[0].ToString();
        }

        private bool validAddress(String stream)
        {
            string pat = @"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+(?:[A-Z]{2}|com|org|net|edu|gov|mil|biz|info|mobi|name|aero|asia|jobs|museum)\b";
            Regex r = new Regex(pat, RegexOptions.IgnoreCase);
            Match m = r.Match(stream);
            if (!m.Success)
                return false;
            else
                return true;
        }

    }
}



/*    {
    Log("Received HELO\n");
    buf+=5;
    //User session established
    m_nStatus=SMTP_STATUS_HELO;
    m_FromAddress.SetAddress("");
    m_nRcptCount=0;
    // Prepare a new message now.
    CreateNewMessage();
    return SendResponse(250);
}
        /*
MAIL
S: 250
F: 552, 451, 452
E: 500, 501, 421
*/
/*
int CMailSession::ProcessMAIL(char *buf, int len)
{
  char address[MAX_ADDRESS_LENGTH+5];
  char *st,*en;
  __w64 int alen;
  if(m_nStatus!=SMTP_STATUS_HELO)
  {
      return SendResponse(503);
  }
  memset(address,0,sizeof(address));
  st=strchr(buf,'<');
  en=strchr(buf,'>');
  st++;
  alen=en-st;
  strncpy(address,st,alen);
  printf("FROM [%s]",address);
  if(!CMailAddress::AddressValid(address))
  {
      return SendResponse(501);
  }
  m_FromAddress.SetAddress(address);
  return SendResponse(250);
}

bool replay(int nResponseType)
{
  String buf;
  int len;
  if(nResponseType==220)
      buf="220 "+DOMAIN_NAME+"%s Welcome to "+APP_TITLE+" "+ APP_VERSION+"\r\n";
  else if(nResponseType==221)
      buf="221 Service closing transmission channel\r\n";
  else if (nResponseType==250) 
      buf="250 OK\r\n";
  else if (nResponseType==354)
      buf="354 Start mail input; end with <CRLF>.<CRLF>\r\n";
  else if(nResponseType==501)
      buf="501 Syntax error in parameters or arguments\r\n"; 
  else if(nResponseType==502)
      buf="502 Command not implemented\r\n"; 
  else if(nResponseType==503)
      buf="503 Bad sequence of commands\r\n"; 
  else if(nResponseType==550)
      buf="550 No such user\r\n";
  else if(nResponseType==551)
      buf="551 User not local. Can not forward the mail\r\n";
  else
      buf=nResponseType+" No description\r\n";

  return buf;
}



      */




